#define SOL_ALL_SAFETIES_ON 1
#include <sol/sol.hpp>
#include "tensor.hpp"

template<class T>
sol::object getter(const Eigen::Tensor<T,4>& vec, sol::table key, sol::this_state L) {
		// we use stack_object for the arguments because we know
		// the values from Lua will remain on Lua's stack,
		// so long we we don't mess with it
		// String keys failed, check for numbers
	sol::object key1 = key[1];
	sol::object key2 = key[2];
	sol::object key3 = key[3];
	sol::object key4 = key[4];

	auto maybe_numeric_key1 = key1.as<sol::optional<int>>();
	auto maybe_numeric_key2 = key2.as<sol::optional<int>>();
	auto maybe_numeric_key3 = key3.as<sol::optional<int>>();
	auto maybe_numeric_key4 = key4.as<sol::optional<int>>();
		if (maybe_numeric_key1&&maybe_numeric_key2&&maybe_numeric_key3&&maybe_numeric_key4) {
			int n1 = *maybe_numeric_key1;
			int n2 = *maybe_numeric_key2;
			int n3 = *maybe_numeric_key3;
			int n4 = *maybe_numeric_key4;
			if( n1 <= vec.dimensions()[0] && n1 >= 1
				&& n2 <= vec.dimensions()[1] && n2 >= 1
				&& n3 <= vec.dimensions()[2] && n3 >= 1
				&& n4 <= vec.dimensions()[3] && n4 >= 1)
				return sol::object(L, sol::in_place, vec(n1-1,n2-1,n3-1,n4-1));
		}
		// No valid key: push nil
		// Note that the example above is a bit unnecessary:
		// binding the variables x and y to the usertype
		// would work just as well and we would not
		// need to look it up here,
		// but this is to show it works regardless
		return sol::object(L, sol::in_place, sol::lua_nil);
}
template<class T,class T1>
void setter(Eigen::Tensor<T,4>& vec, sol::table key, T1 val) {
		// we use stack_object for the arguments because we know
		// the values from Lua will remain on Lua's stack,
		// so long we we don't mess with it
		// String keys failed, check for numbers
	sol::object key1 = key[1];
		sol::object key2 = key[2];
		sol::object key3 = key[3];
		sol::object key4 = key[4];

		auto maybe_numeric_key1 = key1.as<sol::optional<int>>();
		auto maybe_numeric_key2 = key2.as<sol::optional<int>>();
		auto maybe_numeric_key3 = key3.as<sol::optional<int>>();
		auto maybe_numeric_key4 = key4.as<sol::optional<int>>();
			if (maybe_numeric_key1&&maybe_numeric_key2&&maybe_numeric_key3&&maybe_numeric_key4) {
				int n1 = *maybe_numeric_key1;
				int n2 = *maybe_numeric_key2;
				int n3 = *maybe_numeric_key3;
				int n4 = *maybe_numeric_key4;
				if( n1 <= vec.dimensions()[0] && n1 >= 1
					&& n2 <= vec.dimensions()[1] && n2 >= 1
					&& n3 <= vec.dimensions()[2] && n3 >= 1
					&& n4 <= vec.dimensions()[3] && n4 >= 1){
						vec(n1-1,n2-1,n3-1,n4-1) = val;
						return;
		}
	}
	throw "Eigen::Tensor out of range or index error at setter4";
}





template<class T>
sol::object getter3(const Eigen::Tensor<T,3>& vec, sol::table key, sol::this_state L) {
		// we use stack_object for the arguments because we know
		// the values from Lua will remain on Lua's stack,
		// so long we we don't mess with it
		// String keys failed, check for numbers
	sol::object key1 = key[1];
	sol::object key2 = key[2];
	sol::object key3 = key[3];

	auto maybe_numeric_key1 = key1.as<sol::optional<int>>();
	auto maybe_numeric_key2 = key2.as<sol::optional<int>>();
	auto maybe_numeric_key3 = key3.as<sol::optional<int>>();
		if (maybe_numeric_key1&&maybe_numeric_key2&&maybe_numeric_key3) {
			int n1 = *maybe_numeric_key1;
			int n2 = *maybe_numeric_key2;
			int n3 = *maybe_numeric_key3;
			if( n1 <= vec.dimensions()[0] && n1 >= 1
				&& n2 <= vec.dimensions()[1] && n2 >= 1
				&& n3 <= vec.dimensions()[2] && n3 >= 1)
				return sol::object(L, sol::in_place, vec(n1-1,n2-1,n3-1));
		}
		// No valid key: push nil
		// Note that the example above is a bit unnecessary:
		// binding the variables x and y to the usertype
		// would work just as well and we would not
		// need to look it up here,
		// but this is to show it works regardless
		return sol::object(L, sol::in_place, sol::lua_nil);
}
template<class T,class T1>
void setter3(Eigen::Tensor<T,3>& vec, sol::table key, T1 val) {
		// we use stack_object for the arguments because we know
		// the values from Lua will remain on Lua's stack,
		// so long we we don't mess with it
		// String keys failed, check for numbers
	sol::object key1 = key[1];
		sol::object key2 = key[2];
		sol::object key3 = key[3];

		auto maybe_numeric_key1 = key1.as<sol::optional<int>>();
		auto maybe_numeric_key2 = key2.as<sol::optional<int>>();
		auto maybe_numeric_key3 = key3.as<sol::optional<int>>();
			if (maybe_numeric_key1&&maybe_numeric_key2&&maybe_numeric_key3) {
				int n1 = *maybe_numeric_key1;
				int n2 = *maybe_numeric_key2;
				int n3 = *maybe_numeric_key3;
				if( n1 <= vec.dimensions()[0] && n1 >= 1
					&& n2 <= vec.dimensions()[1] && n2 >= 1
					&& n3 <= vec.dimensions()[2] && n3 >= 1){
						vec(n1-1,n2-1,n3-1) = val;
						return;
		}
	}
	throw "Eigen::Tensor out of range or index error at setter3";
}

typedef std::complex<double> complex;

template sol::object getter<double>(const Eigen::Tensor<double,4>& vec, sol::table key, sol::this_state L);
template void setter<double,double>(Eigen::Tensor<double,4>& vec, sol::table key, double val);
template sol::object getter<complex>(const Eigen::Tensor<complex,4>& vec, sol::table key, sol::this_state L);
template void setter<complex,double>(Eigen::Tensor<complex,4>& vec, sol::table key, double val);
template void setter<complex,complex>(Eigen::Tensor<complex,4>& vec, sol::table key, complex val);

template sol::object getter3<double>(const Eigen::Tensor<double,3>& vec, sol::table key, sol::this_state L);
template void setter3<double,double>(Eigen::Tensor<double,3>& vec, sol::table key, double val);

template sol::object getter3<complex>(const Eigen::Tensor<complex,3>& vec, sol::table key, sol::this_state L);
template void setter3<complex,complex>(Eigen::Tensor<complex,3>& vec, sol::table key, complex val);
template void setter3<complex,double>(Eigen::Tensor<complex,3>& vec, sol::table key, double val);


